/*
 * Copyright 2023 devaper
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.github.devaper.pigeon.annotation

import java.lang.reflect.*
import java.util.*

/** 所有注解中属性的信息 */
private typealias AttributesMap = HashMap<Class<*>, Attributes>

/** 别名信息列表 */
private typealias Aliases = LinkedList<ReflectAnnotationMetadata.Alias>
/** 所有注解中别名的信息 */
private typealias AliasesMap = LinkedHashMap<Class<*>, Aliases>

/** 所有注解类型的信息 */
private typealias AnnotationTypeMap = HashMap<Class<*>, ReflectAnnotationMetadata.AnnotationType>

/**
 * 基于反射实现的 [AnnotationMetadata]
 *
 * @param annotations 要解析的注解列表
 */
class ReflectAnnotationMetadata(annotations: Array<Annotation>) : AnnotationMetadata {

    /**
     * 所有注解的属性集合
     *
     * 解析完后只存储每个注解的属性名以及值, 其它多余的信息都是没必要的了
     */
    private val attributesMap: AttributesMap

    init {
        // 将需要的数据创建为局部变量, 避免声明为属性太冗余
        val attributesMap = AttributesMap()
        val aliasesMap = AliasesMap()
        val annotationTypeMap = AnnotationTypeMap()


        resolveAnnotationMembers(
            annotations = annotations,
            attributesMap = attributesMap,
            aliasesMap = aliasesMap,
            annotationTypeMap = annotationTypeMap,
            deep = 0
        )

        handleAliases(
            attributesMap = attributesMap,
            aliasesMap = aliasesMap,
            annotationTypeMap = annotationTypeMap
        )

        this.attributesMap = attributesMap
    }

    /**
     * 解析注解的成员, 内部是递归进行的
     *
     * 此处会做以下操作
     * - 为每个注解创建 [AnnotationType] 存入 [AnnotationTypeMap]
     * - 取出所有注解的属性以及值存入 [AttributesMap]
     * - 取出所有注解标记了 [AliasFor] 的属性, 并创建好 [Alias] 存入 [AliasesMap]
     *
     * 这个方法完成后应该得到的就是所有注解的属性以及值、AnnotationType、AliasFor信息
     *
     * @param deep 代表深度, 最后Alias是要根据注解的深度排序的, 越深的越后处理
     */
    private fun resolveAnnotationMembers(
        annotations: Array<Annotation>,
        attributesMap: AttributesMap,
        aliasesMap: AliasesMap,
        annotationTypeMap: AnnotationTypeMap,
        deep: Int
    ) {
        annotations.forEach { annotation ->
            val annotationJClass = annotation.annotationClass.java
            // 解析过的不在处理
            if (attributesMap.containsKey(annotationJClass)) {
                return@forEach
            }
            val annotationType = AnnotationType(annotationJClass)
            val attributes = Attributes(annotationType.members.size)
            var aliases: Aliases? = null

            // 处理当前注解每一个成员
            for (member in annotationType.members) {
                // 处理属性值的获取
                attributes[member.name] = member.invoke(annotation)

                // 处理AliasFor
                // 将aliasFor的信息暂存起来, 等所有注解的值取完, 再去解析AliasFor
                member.getAnnotation(AliasFor::class.java)?.let { aliasFor ->
                    if (aliases == null) {
                        aliases = LinkedList()
                    }
                    aliases!!.add(Alias(
                        attribute = member.name,
                        destAnnotation = aliasFor.annotation.firstOrNull()?.java,
                        destAttribute = aliasFor.value.takeIf { it.isNotEmpty() }
                    ))
                }
            }

            // 注解解析完成, 暂存起来
            attributesMap[annotationJClass] = attributes
            annotationTypeMap[annotationJClass] = annotationType
            if (!aliases.isNullOrEmpty()) {
                aliasesMap[annotationJClass] = aliases!!
            }

            // 递归解析
            resolveAnnotationMembers(
                annotations = annotationJClass.annotations,
                attributesMap = attributesMap,
                aliasesMap = aliasesMap,
                annotationTypeMap = annotationTypeMap,
                deep = deep + 1
            )
        }
    }

    /**
     * 处理Alias信息
     *
     * 这里负责的就是通过Alias信息将注解中的属性拷贝到指定的注解中
     */
    private fun handleAliases(
        attributesMap: AttributesMap,
        aliasesMap: AliasesMap,
        annotationTypeMap: AnnotationTypeMap
    ) {
        // 遍历所有注解的Alias
        for ((annotationJClass, aliases) in aliasesMap) {
            // 当前注解的AnnotationType
            val annotationType = annotationTypeMap[annotationJClass]!!
            // 当前注解的所有属性
            val attributes = attributesMap[annotationJClass]!!
            // 处理当前注解所有的Alias
            aliases.forEach { alias ->
                // 获取当前属性值与默认值, 如果是默认值则跳过Alias处理
                val value = attributes[alias.attribute]!!
                val defaultValue = annotationType.memberDefaults[alias.attribute]
                if (value == defaultValue) {
                    return@forEach
                }
                // 通过设置的annotation选择最终值输出到哪个attributes中
                // 没有设置就是去给当前注解属性赋值, 设置了就是给目标注解属性赋值
                val targetAnnotationJClass = alias.destAnnotation ?: annotationJClass
                copyValue(
                    sourceAnnotationJClass = annotationJClass,
                    destAnnotationJClass = targetAnnotationJClass,
                    sourceValue = value,
                    destAttributes = attributesMap[targetAnnotationJClass],
                    destAttributeName = alias.destAttribute ?: alias.attribute
                )
            }
        }
    }

    /**
     * 拷贝值到目标 [Attributes] 中
     *
     * 如果类型一致, 那么直接拷贝, 如果类型不一致那么尝试转换
     * - String -> Class
     * - 任何类型的数组 -> 任何类型的值 (只会取出第一项)
     * - 任何类型的值 -> 任何类型的数组 (单个元素的数组)
     * 在类型转换的时候, 所有类型都会被转为String再进行转换
     */
    private fun copyValue(
        sourceAnnotationJClass: Class<*>,
        destAnnotationJClass: Class<*>,
        sourceValue: Any,
        destAttributes: Attributes?,
        destAttributeName: String
    ) {
        // 目标是无效的, 跳过而不是抛出异常
        if (destAttributes == null) return
        // 获取目标原本的值, 这只是拿出来进行类型比较
        val oldValue = destAttributes[destAttributeName] ?: throw NoSuchMethodError(destAttributeName)

        val sourceValueType = sourceValue::class.java
        val targetValueType = oldValue::class.java

        // 类型一致直接拷贝
        if (sourceValueType == targetValueType) {
            destAttributes[destAttributeName] = sourceValue
            return
        }

        // 类型不一致尝试进行类型转换
        @Suppress("unchecked_cast")
        try {
            if (sourceValueType.isArray) {
                val sourceArray = sourceValue as Array<Any>
                val sourceArrayComponent = sourceValueType.componentType
                if (targetValueType.isArray) {
                    val targetValueComponent =  targetValueType.componentType
                    val destArray = java.lang.reflect.Array.newInstance(targetValueComponent, sourceArray.size) as Array<Any>
                    sourceArray.forEachIndexed { index, _ ->
                        destArray[index] = convert(sourceArray[index], targetValueComponent)!!
                    }
                } else {
                    // 类型一致, 取出元素放入即可
                    destAttributes[destAttributeName] =
                        if (sourceArrayComponent == targetValueType) sourceArray.first() else convert(sourceArray.first(), targetValueType)!!
                }
            } else {
                if (targetValueType.isArray) {
                    val targetArrayComponent = targetValueType.componentType
                    val convertedValue = if (sourceValueType == targetArrayComponent) {
                        sourceValue
                    } else {
                        convert(sourceValue, targetArrayComponent)!!
                    }
                    val array = java.lang.reflect.Array.newInstance(targetArrayComponent, 1)
                    java.lang.reflect.Array.set(array, 0, convertedValue)
                    destAttributes[destAttributeName] = array
                } else {
                    destAttributes[destAttributeName] = convert(sourceValue, targetValueType)!!
                }
            }
        } catch (e: Throwable) {
            throw NullPointerException("Cannot handle type [${sourceAnnotationJClass.canonicalName}(${sourceValueType.canonicalName})] to type [${destAnnotationJClass.canonicalName}(${targetValueType.canonicalName})] conversions").initCause(e)
        }
    }

    /**
     * 转换类型
     *
     * @param value 当前值
     * @param targetType 目标类型
     * @return 转换后的值, 不支持则返回null
     */
    private fun convert(value: Any, targetType: Class<*>): Any? {
        val valueType = value::class.java
        if (valueType == targetType) {
            return value
        }
        // 其它类型的处理将值先转为String再处理, 简化代码
        val string = value.toString()
        return when(targetType) {
            String::class.java -> string
            Int::class.java -> string.toInt()
            Boolean::class.java -> string == "1" || string.lowercase() == "true" || string.lowercase() == "yes"
            Class::class.java -> Class.forName(string)
            Float::class.java -> string.toFloat()
            Double::class.java -> string.toDouble()
            Char::class.java -> string.first()
            Byte::class.java -> string.toByte()
            Short::class.java -> string.toShort()
            Long::class.java -> string.toLong()
            else -> null
        }

    }

    override fun contains(annotationJClass: Class<*>): Boolean {
        return attributesMap.containsKey(annotationJClass)
    }

    override fun get(annotationJClass: Class<*>): Attributes? {
        return attributesMap[annotationJClass]
    }

    /**
     * 负责根据注解类型解析注解中的基本信息, 这里包含了注解中所有的Method、属性默认值
     */
    internal class AnnotationType(annotationJClass: Class<*>) {
        /**
         * 成员方法
         */
        val members: List<Method>

        /**
         * 属性默认值, key=属性名/方法名 value=默认值
         */
        val memberDefaults: Map<String, Any>

        init {
            val methods = annotationJClass.methods.filter { it.declaringClass != Annotation::class.java }
            members = methods
            memberDefaults = methods.associateByTo(HashMap(methods.size), { it.name }, { it.defaultValue })
        }
    }

    /**
     * 别名信息, 首次解析取出所有注解属性的同时会为所有的 [AliasFor] 创建该对象, 暂存然后统一处理
     */
    internal data class Alias(
        /** 当前注解中的属性 */
        val attribute: String,
        /** [AliasFor] 指向的注解, 没有指向为空 */
        val destAnnotation: Class<*>?,
        /** [AliasFor] 指向的属性名, 没有指向为空 */
        val destAttribute: String?
    )
}